changed mcp2515 driver to use nonblocking spi - #817
Conversation
rleh
left a comment
There was a problem hiding this comment.
Nice!
Thanks for taking care of this!
| } | ||
|
|
||
| template <typename SPI, typename CS, typename INT> | ||
| modm::ResumableResult<bool> |
There was a problem hiding this comment.
Shouldn't update() return modm::ResumableResult<void>?
Because nobody (in the main loop) will handle the return value...
There was a problem hiding this comment.
it's just the return value of sendMessage indicating if a message was sent this cycle ... not really useful but i thought why not :D ... iwill remove it
dc57d6f to
03639b4
Compare
|
Another addition: This driver thould use the Off-topic discussion: In the case mentioned above the SPI clock has to be limited to 1MHz or 2MHz (ADIS16470 limitation), but for the MCP2515 a higher SPI clock would be desirable. This probably affects many (SPI) drivers, what would be the most elegant way to solve this in modm? |
That requires to reconfigure SPI timings when the |
|
@rleh I have quickly hacked together a sub-optimal implementation for switching the SPI baudrate with multiple devices. This is just a rough proof of concept for STM32 only and makes changes to the https://github.com/chris-durand/modm/tree/wip_spi_user_conf_handler |
I don't know what that is bit I'll look into it tomorrow (after our work meeting stuff) |
The configuration handler is actually quite simple. If you have multiple devices on one bus that require different SPI modes you'll need to reconfigure the mode whenever you run a transfer on the next device. You can define a callback that configures the required parameters and modm will call it automatically when needed. Here is an example. EDIT: if you mean the |
Yeah the second one .. you explained what it does quite well, thanks :) |
@rleh @chris-durand |
|
SpiDevice test seems to be broken? Or did I do something wrong? |
That is because of my experimental commit that was cherry-picked. It is only a proof of concept for STM32. Remove it and everything will be fine :) |
|
I think it would actually make sense to force all SPI drivers to always set all "relevant" SPI options, everything else doesn't work consistently and leads to strange behavior, especially when multiple SPI drivers access SPI in different orders one after the other. The "relevant" SPI options:
To enforce this I would suggest to implement these three values (with usable default values) in addition to the configurationHandler in the The configurationHandler is optional and should only be used to configure additional IOs/multiplexers/... . What do you think @chris-durand? |
Well I dont see why the SpiDevice is currently not a default base class with mandatory (pure virtual) overrides
|
I agree that it makes sense to make it mandatory for the SPI modes. I am not sure how it would work for the frequency. The frequency is a compile time template parameter in the SPI master because we want to verify it at compile-time. We would need a second API that allows runtime values without compile-time checking to make it configurable. Furthermore the clock configuration could require some other platform specific settings on future platforms. I would prefer to decouple it from the device drivers. I would rather let the user provide a call back for the speed configuration that is called when the SPI device is acquired. |
I think there is no need for runtime polymorphism and dynamic dispatch for that use case. Couldn't we just pass a configuration as a non-type template parameter to the It's not a real is-a relationship for let's say an MCP2515 and a generic SPI device. I would argue a MCP2515 is not a specific version of an SPI interface to be used as such. SPI is an implementation detail, in its public interface it's a CAN interface. |
Looking at the STM32 SPI driver it is actually the opposite. I would change the |
static polymorph is a thing :/ |
839b40a to
f94d31a
Compare
Yes, but it is normally not done using Which virtual functions are you thinking of when you said |
chris-durand
left a comment
There was a problem hiding this comment.
Thanks again for refactoring this driver!
|
|
||
| using namespace mcp2515; | ||
|
|
||
| while(!this->acquireMaster()){}; |
There was a problem hiding this comment.
This will block forever if the SPI is acquired by another driver. The code that is releasing the SPI won't run with the cooperative resumable function execution model when you busy-wait in this loop. This function needs to be resumable for it to work. Then you can do RF_WAIT_UNTIL(this->acquireMaster());.
When we have gotten rid of resumable functions in favor of fibers everything will be better I assume...
There was a problem hiding this comment.
it's in the part i left blocking (initialization) ... the whole init could be changed to be non blocking, but that's probably not important
There was a problem hiding this comment.
Maybe create a initializeWithPrescalerBlocking() function that calls RF_CALL_BLOCKING(initializeWithPrescaler(...))?
| void | ||
| modm::Mcp2515<SPI, CS, INT>::writeRegister(uint8_t address, uint8_t data) | ||
| { | ||
| while(!this->acquireMaster()){}; |
There was a problem hiding this comment.
Same as above. The program will get stuck here if the SpiMaster has been acquired already.
There was a problem hiding this comment.
these functions are called from initialize(), which should probably be blocking (I kept it blocking)
There was a problem hiding this comment.
Imagine the following situation: You use the same SpiMaster with another driver and then initialize the Mcp2515 while another transaction is running from the other device. Then the initialize() will deadlock forever. Even if that situation is not that common, it will cause nasty bugs.
If I am not mistaken every other initialization function that writes SPI / I²C in modm drivers is resumable. If something should be forced to execute blocking there is always RF_CALL_BLOCKING().
There was a problem hiding this comment.
Yeah I am aware of that limitation .. I am still playing around with other stuff so I will try to get the init stuff properly set up later :) just have to do other things for a while :D
either pure virtual or a crtp adapter, where release and acquire master is done in every spi command automatically. you mutex bus access (which is a wise thing to do) . The fact that a driver can decide to NOT do that is really weird to me :D |
Ah, now I understand what you would like to do. Enforcing that the lock on the SPI is held while it is used sounds like a good idea. |
When I added the config handler, it was intended to be set by the application and NOT the driver, to avoid exactly this issue. The developer knows what other devices are on the same bus and thus knows what settings to change. If you only have one device on the bus, you don't need the config handler at all, it is really supposed to be only a user config handler, not a driver config handler. |
|
Yeah, I'm fine with that too. It's more thought out with the config struct. |
|
this whole discussion is now geared towwards changing the whole device / driver api ... as I see it that is a necessary step because (as I said earlier) the api seems to be a little messy. But does that mean this has to be done in this specific pr? my mcp driver in general:
does this matter now anymore? |
This does not have to happen in this PR, in my view it is even nicer to do it in a separate PR. |
|
I guess @kikass13 might be asking if we should change the API of the MCP2515 driver, not the |
|
@chris-durand currently i like the fact that this thing is static. I dislike the "mix" but i can live with it ... I have some software running this thing currently (successfully), and I really depend on the fact, that the can interfaces can be used statically.
At least the current implementation works for me
|
Can't you in the class where you passed the static class as a template argument instantiate an object of the template argument? Then it works with both static and non-static classes. |
sure, i was just trying to explain that the old "design" could still be valid. @rleh you blocked my initial changes because I changed the API, so changing it back again now seems kind of redundant :D |
| #ifndef MODM_MCP2515_HPP | ||
| #error "Don't include this file directly, use 'mcp2515.hpp' instead!" | ||
| #endif | ||
|
|
| if(not modm_assert_continue_ignore(rxQueue.push(messageBuffer_), "mcp2515.can.tx", | ||
| "CAN transmit software buffer overflowed!", 1)){ | ||
| /// ignore | ||
| } |
There was a problem hiding this comment.
Remove the if statement if the body is empty.
There was a problem hiding this comment.
well it was there before ... it shows intent .. so i kept it for now
|
@rleh i have changed all the points you made (hopefully to your satisfaction) :) |
814c603 to
5aa143e
Compare
rleh
left a comment
There was a problem hiding this comment.
- Inside
initializeWithPrescaler()SPI access is performed without locking the bus (usingSpiDevice::acquireMaster()/::releaseMaster()) writeRegister(),readRegister()andbitModify()also perform SPI access without usingSpiDevice::acquireMaster()/::releaseMaster()and are not resumable
You have to switch from the update() resumable function to a protothread, and inside the protothread you can then do the initialization stuff, triggered by a flag set from the initialize() function.
| <option name="modm:driver:mcp2515:buffer.tx">32</option> | ||
| <option name="modm:driver:mcp2515:buffer.rx">32</option> |
There was a problem hiding this comment.
32 is already the default value. Remove?
There was a problem hiding this comment.
not a good idea, this shows intent .. nobody would know that this exists otherwise
There was a problem hiding this comment.
Everybody that reads the documentation or uses lbuild discover-options will know about it...
| namespace mcp2515{ | ||
| namespace options{ | ||
|
|
||
| %% if options["buffer.tx"] > 0 |
There was a problem hiding this comment.
| %% if options["buffer.tx"] > 0 |
buffer.tx is enforced to be within range 1..(2^16-2) by the lbuild module.
| #ifndef MODM_MCP2515_HPP | ||
| #error "Don't include this file directly, use 'mcp2515.hpp' instead!" | ||
| #endif | ||
|
|
| } | ||
|
|
||
| template <typename SPI, typename CS, typename INT> | ||
| modm::ResumableResult<bool> |
| template <typename SPI, typename CS, typename INT> | ||
| modm::ResumableResult<bool> | ||
| modm::Mcp2515<SPI, CS, INT>::update(){ | ||
| using namespace mcp2515; | ||
|
|
||
| RF_BEGIN(); |
There was a problem hiding this comment.
Shouldn't that rather be a Protothread and not a resumable function?
There was a problem hiding this comment.
dunno what you mean. As the api is right now (static), there exists a protothread in the user code (which you told me in a prior version of this driver update, that it should be consistent to the general driver api) ... which one is it?
There was a problem hiding this comment.
Hint:
| template <typename SPI, typename CS, typename INT> | |
| modm::ResumableResult<bool> | |
| modm::Mcp2515<SPI, CS, INT>::update(){ | |
| using namespace mcp2515; | |
| RF_BEGIN(); | |
| template <typename SPI, typename CS, typename INT> | |
| void | |
| modm::Mcp2515<SPI, CS, INT>::run() | |
| { | |
| PT_BEGIN(); | |
| //... |
There was a problem hiding this comment.
then the user hjas two protothreads runnign inside each other?
why should the thread be in the driver instead of the user deciding when or in which context update is called?
Hint:
- not making a point clear / answering the implicity question of
what do you actually want to do and whydoes not help me :D
| if ((readStatus(READ_STATUS) & (TXB2CNTRL_TXREQ | TXB1CNTRL_TXREQ | TXB0CNTRL_TXREQ)) == | ||
| RF_BEGIN(); | ||
|
|
||
| tempS = true; |
There was a problem hiding this comment.
Meaningful variable name or explanation please...
| ready = false; | ||
| } | ||
| return ready; |
There was a problem hiding this comment.
| ready = false; | |
| } | |
| return ready; | |
| return false; | |
| } | |
| return true; |
| modm::Mcp2515<SPI, CS, INT>::writeRegister(uint8_t address, uint8_t data) | ||
| { | ||
| chipSelect.reset(); | ||
|
|
There was a problem hiding this comment.
Still a lot of unnecessary whitespace change in this part of the file.
|
@kikass13 Please stop marking my change requests which are not resolved as resolved. |
0ff3eef to
7959cdc
Compare
7959cdc to
3b3ee65
Compare
3b3ee65 to
3db3bcd
Compare
attachConfigurationHandlerfor switching device spi typesor discovery boardattachConfigurationHandlerfor switching device clock speeds